﻿namespace Patterns4Net.Tests.PatternEnforcer.Core.Patterns.Regression
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using NUnit.Framework;
    using Patterns4Net.Core.CecilExtensions;
    using Patterns4Net.Core.Patterns;
    using Patterns4Net.PatternEnforcer.Core.Patterns;

    [TestFixture]
    public class SingletonCheckerTest : PatternCheckerTestBase
    {
        [Test]
        [TestCase(typeof(PseudoSingletonWithPublicConstructor))]
        [TestCase(typeof(SingletonWithStaticReadonlyField))]
        [TestCase(typeof(SingletonWithNestedClass))]
        [TestCase(typeof(SingletonWithGetInstanceMethod))]
        [TestCase(typeof(SingletonThatDoesNotInitializesInstanceInStaticConstructor))]
        [TestCase(typeof(SingletonThatContainsOtherCallToCtorInNestedClass))]
        public void RunAllTest(Type type)
        {
            this.TestChecker(
                new SingletonChecker(), 
                type,
                new Singleton(type.GetTypeDefinition()));
        }

        [PatternTest(false)]
        public class PseudoSingletonWithPublicConstructor
        {
            public static readonly PseudoSingletonWithPublicConstructor Instance =
                new PseudoSingletonWithPublicConstructor();

            public PseudoSingletonWithPublicConstructor()
            {
            }
        }

        [PatternTest(true)]
        public class SingletonWithStaticReadonlyField
        {
            private static readonly SingletonWithStaticReadonlyField instance = new SingletonWithStaticReadonlyField();

            private SingletonWithStaticReadonlyField()
            {
            }

            public static SingletonWithStaticReadonlyField Instance
            {
                get
                {
                    return instance;
                }
            }
        }

        [PatternTest(false)]
        public class SingletonThatDoesNotInitializesInstanceInStaticConstructor
        {
            private static SingletonThatDoesNotInitializesInstanceInStaticConstructor instance;

            private SingletonThatDoesNotInitializesInstanceInStaticConstructor()
            {
            }

            public static void UpdateMyInstance()
            {
                instance = new SingletonThatDoesNotInitializesInstanceInStaticConstructor();
            }
        }

        [PatternTest(false)]
        public class SingletonThatContainsOtherCallToCtorInNestedClass
        {
            private static SingletonThatContainsOtherCallToCtorInNestedClass instance = new SingletonThatContainsOtherCallToCtorInNestedClass();

            private SingletonThatContainsOtherCallToCtorInNestedClass()
            {
            }

            public class Nested
            {
                public static void UpdateMyInstance()
                {
                    instance = new SingletonThatContainsOtherCallToCtorInNestedClass();
                }
            }
        }

        [PatternTest(true)]
        public class SingletonWithGetInstanceMethod
        {
            private static int instanceAccessCounter = 0;

            private static int justToDoSomething = 0;

            private static SingletonWithGetInstanceMethod instance;

            private SingletonWithGetInstanceMethod()
            {
            }

            public static SingletonWithGetInstanceMethod Instance
            {
                get
                {
                    int someLocalField = 3;
                    instanceAccessCounter++;
                    if (instance == null)
                    {
                        instance = new SingletonWithGetInstanceMethod();
                        justToDoSomething++;
                        someLocalField++;
                    }

                    justToDoSomething += someLocalField;
                    return instance;
                }
            }
        }

        [PatternTest(true)]
        public class SingletonWithNestedClass
        {
            private SingletonWithNestedClass()
            {
            }

            public static SingletonWithNestedClass Instance
            {
                get
                {
                    return Nested.Instance;
                }
            }

            private static class Nested
            {
                [SuppressMessage("Microsoft.StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", 
                    Justification = "We dont care here")]
                public static SingletonWithNestedClass Instance = new SingletonWithNestedClass();
            }
        }
    }
}